/********************************************************************************
 * Copyright (c) 2019-2020 [Open Lowcode SAS](https://openlowcode.com/)
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0 .
 *
 * SPDX-License-Identifier: EPL-2.0
 ********************************************************************************/

package org.openlowcode.design.data.properties.basic;

import java.io.IOException;
import java.util.ArrayList;

import org.openlowcode.design.data.ChoiceField;
import org.openlowcode.design.data.DataAccessMethod;
import org.openlowcode.design.data.DataObjectDefinition;
import org.openlowcode.design.data.MethodAdditionalProcessing;
import org.openlowcode.design.data.MethodArgument;
import org.openlowcode.design.data.StringField;
import org.openlowcode.design.data.TimestampField;
import org.openlowcode.design.data.TransitionChoiceCategory;
import org.openlowcode.design.data.argument.BooleanArgument;
import org.openlowcode.design.data.argument.ObjectArgument;
import org.openlowcode.design.data.argument.ObjectIdArgument;
import org.openlowcode.design.data.argument.StringArgument;
import org.openlowcode.design.data.properties.workflow.ObjectToAuthorityMapper;
import org.openlowcode.design.generation.SourceGenerator;
import org.openlowcode.design.generation.StringFormatter;
import org.openlowcode.design.module.Module;
import org.openlowcode.module.system.design.SystemModule;

/**
 * A simple task workflow will create a task per object that can be sent to
 * various teams or people. Main behaviour is:
 * <ul>
 * <li>When the task is selected for work by a user, the object will move to
 * default working state</li>
 * <li>when the task is finished, the object will move to the default final
 * state</li>
 * </ul>
 * 
 * @author <a href="https://openlowcode.com/" rel="nofollow">Open Lowcode
 *         SAS</a>
 *
 */
public class SimpleTaskWorkflow
		extends
		ObjectWithWorkflow {

	public static final String EMAIL_NONE = null;
	public static final String EMAIL_NOW = "NOW";
	public static final String EMAIL_DELAY15MIN = "D15M";
	public static final String EMAIL_DELAY2H = "D2H";
	public static final String EMAIL_DAILY = "DAY";
	public static final String EMAIL_WEEKLY = "WEEKLY";

	private ObjectToAuthorityMapper authorityselection;
	private TransitionChoiceCategory lifecyclehelper;
	private String taskmessage = null;
	private String emailtype;
	private DataObjectDefinition objectworkflowreport;

	/**
	 * @return the workflow report object that will be automatically generated by
	 *         the system. It is used to have stats per week
	 */
	public DataObjectDefinition getWorkflowReportObject() {
		return objectworkflowreport;
	}

	/**
	 * Creates a simple task workflow on the given object
	 * 
	 * @param authorityselection the rule used to send objects to users (sent to all
	 *                           users in a given authority)
	 * @param emailtype          type of e-mail notification, either stacked or sent
	 *                           real-time
	 */
	public SimpleTaskWorkflow(ObjectToAuthorityMapper authorityselection, String emailtype) {
		super("SIMPLETASKWORKFLOW");
		this.authorityselection = authorityselection;
		if (this.authorityselection == null)
			throw new RuntimeException("SimpleTask workflow needs authority selection");

		boolean emailtypevalue = false;
		if (emailtype == null)
			emailtypevalue = true;
		if (emailtype.equals(EMAIL_NOW))
			emailtypevalue = true;
		if (emailtype.equals(EMAIL_DELAY15MIN))
			emailtypevalue = true;
		if (emailtype.equals(EMAIL_DELAY2H))
			emailtypevalue = true;
		if (emailtype.equals(EMAIL_DAILY))
			emailtypevalue = true;
		if (emailtype.equals(EMAIL_WEEKLY))
			emailtypevalue = true;

		if (!emailtypevalue)
			throw new RuntimeException("email type is not valid, please refer to the class static string " + emailtype);
		this.emailtype = emailtype;
	}

	@Override
	public void controlAfterParentDefinition() {
		Numbered numbered = (Numbered) parent.getPropertyByName("NUMBERED");
		if (numbered == null)
			throw new RuntimeException("Object should have numbered to have simple task workflow");
		Named named = (Named) parent.getPropertyByName("NAMED");
		if (named == null)
			throw new RuntimeException("Object should have named to have simple task workflow");
		Lifecycle lifecycle = (Lifecycle) parent.getPropertyByName("LIFECYCLE");
		if (lifecycle == null)
			throw new RuntimeException("Object should have lifecycle to have simple task workflow");

		// defines the object for workflow report
		objectworkflowreport = new DataObjectDefinition(parent.getName() + "WFREPORT",
				parent.getLabel() + " Workflow Report", parent.getOwnermodule(), true);
		this.addExternalObjectProperty(objectworkflowreport, new TransientParent("PARENTID", parent));
		this.addExternalObjectProperty(objectworkflowreport,
				new TransientParent("TASK", SystemModule.getSystemModule().getTask()));

		objectworkflowreport.addField(new StringField("AUTHORITY", "Authority",
				"Authority the object was assign to for resolution", 64, 300));
		objectworkflowreport.addField(new StringField("NUMBER", numbered.getNumberLabel(),
				"Number of object " + parent.getLabel() + " on which workflow is runnning", 64, 300));
		objectworkflowreport.addField(new StringField("NAME", named.getNameLabel(),
				"Name of object " + parent.getLabel() + " on which workflow is runnning", 64, 300));
		objectworkflowreport.addField(new ChoiceField("STATUS", "Status",
				"Status of object " + parent.getLabel() + " on which workflow is runnning",
				lifecycle.getLifecycleHelper(), 300));
		objectworkflowreport.addField(new ChoiceField("ASSIGNEDSINGLE", "Assigned To Single User", "",
				SystemModule.getSystemModule().getBooleanChoice()));
		objectworkflowreport.addField(
				new ChoiceField("ACCEPTED", "Accepted", "", SystemModule.getSystemModule().getBooleanChoice()));
		objectworkflowreport.addField(new StringField("ASSIGNEE", "Assignee Name", "Assignee Name", 128, 200));
		objectworkflowreport
				.addField(new TimestampField("CREATEDDATE", "Task Opened On", "", TimestampField.INDEXTYPE_NONE));
		objectworkflowreport
				.addField(new TimestampField("LASTASSIGNMENT", "Last Assignment", "", TimestampField.INDEXTYPE_NONE));
		objectworkflowreport
				.addField(new TimestampField("TARGETDATE", "Task Target Date", "", TimestampField.INDEXTYPE_NONE));

		lifecyclehelper = lifecycle.getTransitionChoiceCategory();
		TransitionChoiceCategory lifecycledefinition = lifecycle.getTransitionChoiceCategory();
		if (!lifecycledefinition.hasFinalChoice())
			throw new RuntimeException("lifecycle should have final choice to have a simple task workflow");
		if (!lifecycledefinition.hasDefaultWorkingChoice())
			throw new RuntimeException("lifecycle should have default working choice to have a simple task workflow");

		this.addDependentProperty(lifecycle);

		MethodAdditionalProcessing objectcreationprocessing = new MethodAdditionalProcessing(false,
				lifecycle.getDependentPropertyUniqueIdentified().getStoredObject().getDataAccessMethod("INSERT"));
		this.addMethodAdditionalProcessing(objectcreationprocessing);
		MethodAdditionalProcessing statechangepostprocessing = new MethodAdditionalProcessing(false,
				lifecycle.getDataAccessMethod("CHANGESTATE"));
		this.addMethodAdditionalProcessing(statechangepostprocessing);
		DataAccessMethod processtask = new DataAccessMethod("PROCESSTASK", null, false);
		processtask.addInputArgument(new MethodArgument("OBJECT", new ObjectArgument("OBJECT", parent)));
		processtask.addInputArgument(
				new MethodArgument("TASKID", new ObjectIdArgument("TASKID", SystemModule.getSystemModule().getTask())));
		processtask.addInputArgument(new MethodArgument("USERID",
				new ObjectIdArgument("USERID", SystemModule.getSystemModule().getAppuser())));
		processtask.addInputArgument(new MethodArgument("TASKCHOICE",
				new ObjectIdArgument("TASKCHOICEID", SystemModule.getSystemModule().getTaskChoice())));
		processtask.addInputArgument(new MethodArgument("COMMENT", new StringArgument("COMMENT", 2000)));

		this.addDataAccessMethod(processtask);
		DataAccessMethod accepttask = new DataAccessMethod("ACCEPTTASK", null, false);
		accepttask.addInputArgument(new MethodArgument("OBJECT", new ObjectArgument("OBJECT", parent)));
		accepttask.addInputArgument(
				new MethodArgument("TASKID", new ObjectIdArgument("TASKID", SystemModule.getSystemModule().getTask())));
		accepttask.addInputArgument(new MethodArgument("USERID",
				new ObjectIdArgument("USERID", SystemModule.getSystemModule().getAppuser())));
		this.addDataAccessMethod(accepttask);
		DataAccessMethod rejecttask = new DataAccessMethod("REJECTTASK", null, false);
		rejecttask.addInputArgument(new MethodArgument("OBJECT", new ObjectArgument("OBJECT", parent)));
		rejecttask.addInputArgument(
				new MethodArgument("TASKID", new ObjectIdArgument("TASKID", SystemModule.getSystemModule().getTask())));
		rejecttask.addInputArgument(new MethodArgument("USERID",
				new ObjectIdArgument("USERID", SystemModule.getSystemModule().getAppuser())));
		this.addDataAccessMethod(rejecttask);

		DataAccessMethod savetaskcomment = new DataAccessMethod("SAVETASKCOMMENT", null, false);
		savetaskcomment.addInputArgument(new MethodArgument("OBJECT", new ObjectArgument("OBJECT", parent)));
		savetaskcomment.addInputArgument(
				new MethodArgument("TASKID", new ObjectIdArgument("TASKID", SystemModule.getSystemModule().getTask())));
		savetaskcomment.addInputArgument(new MethodArgument("USERID",
				new ObjectIdArgument("USERID", SystemModule.getSystemModule().getAppuser())));
		savetaskcomment.addInputArgument(new MethodArgument("COMMENT", new StringArgument("COMMENT", 2000)));
		this.addDataAccessMethod(savetaskcomment);

		DataAccessMethod canaccepttask = new DataAccessMethod("CANACCEPTTASK", new BooleanArgument("RESULT"), false);
		canaccepttask.addInputArgument(new MethodArgument("OBJECT", new ObjectArgument("OBJECT", parent)));
		canaccepttask.addInputArgument(
				new MethodArgument("TASKID", new ObjectIdArgument("TASKID", SystemModule.getSystemModule().getTask())));
		canaccepttask.addInputArgument(new MethodArgument("USERID",
				new ObjectIdArgument("USERID", SystemModule.getSystemModule().getAppuser())));
		this.addDataAccessMethod(canaccepttask);

		DataAccessMethod reassigntask = new DataAccessMethod("REASSIGNTASK", null, false);
		reassigntask.addInputArgument(new MethodArgument("OBJECT", new ObjectArgument("OBJECT", parent)));
		reassigntask.addInputArgument(
				new MethodArgument("TASKID", new ObjectIdArgument("TASKID", SystemModule.getSystemModule().getTask())));
		reassigntask.addInputArgument(new MethodArgument("USERID",
				new ObjectIdArgument("USERID", SystemModule.getSystemModule().getAppuser())));
		reassigntask.addInputArgument(new MethodArgument("NEWUSERID",
				new ObjectIdArgument("NEWUSERID", SystemModule.getSystemModule().getAppuser())));
		reassigntask.addInputArgument(new MethodArgument("COMMENT", new StringArgument("COMMENT", 2000)));
		this.addDataAccessMethod(reassigntask);

		DataAccessMethod canrejecttask = new DataAccessMethod("CANREJECTTASK", new BooleanArgument("RESULT"), false);
		canrejecttask.addInputArgument(new MethodArgument("OBJECT", new ObjectArgument("OBJECT", parent)));
		canrejecttask.addInputArgument(
				new MethodArgument("TASKID", new ObjectIdArgument("TASKID", SystemModule.getSystemModule().getTask())));
		canrejecttask.addInputArgument(new MethodArgument("USERID",
				new ObjectIdArgument("USERID", SystemModule.getSystemModule().getAppuser())));

		this.addDataAccessMethod(canrejecttask);

		this.addChoiceCategoryHelper("LIFECYCLEHELPER", lifecyclehelper);
		this.taskmessage = "By accepting this task, you commit to conclude all actions to close the "
				+ parent.getLabel() + ".";
		authorityselection.setTaskMessage(taskmessage);
	}

	@Override
	public String[] getPropertyInitMethod() {
		String[] returnvalues = new String[0];
		return returnvalues;
	}

	@Override
	public String[] getPropertyExtractMethod() {
		return new String[0];
	}

	@Override
	public ArrayList<DataObjectDefinition> getExternalObjectDependence() {
		return null;
	}

	@Override
	public void setFinalSettings() {

	}

	@Override
	public String getJavaType() {

		return null;
	}

	@Override
	public void writeDependentClass(SourceGenerator sg, Module module) throws IOException {
		sg.wl("import org.openlowcode.module.system.data.Task;");
		sg.wl("import org.openlowcode.module.system.data.Taskchoice;");
		sg.wl("import org.openlowcode.module.system.data.Appuser;");
		sg.wl("import " + module.getPath() + ".data.choice."
				+ StringFormatter.formatForJavaClass(lifecyclehelper.getName()) + "ChoiceDefinition;");

	}

	@Override
	public String getPropertyHelperName() {
		return StringFormatter.formatForJavaClass(this.getParent().getName()) + "SimpleTaskWorkflowHelper";
	}

	@Override
	public void generatePropertyHelperToFile(SourceGenerator sg, Module module) throws IOException {
		authorityselection.setEmailDelayType(emailtype);
		authorityselection.generateSingleTaskPropertyHelperToFile(sg, module, parent);
	}

	@Override
	public String[] getPropertyDeepCopyStatement() {

		return null;
	}
}
